package ac.uk.qmul.gemom.scenario;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.MenuShortcut;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Enumeration;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTree;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.filechooser.FileFilter;
import javax.swing.tree.TreeCellRenderer;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;


import com.qsphere.boundary.gemom.demo.GEMOMDemo;

/*
* COPYRIGHT DISCLAIMER
* Synopsis: All the source codes presented in this document are intellectual
* properties of Queen Mary, University of London.
* 
* @year 2010
* @author Peng Jiang, John Bigham, Jiayi Wu Jinfu Wang------------QMUL
* @version 1.0 
*/

public class ClientControl extends JFrame{

	public static int client_num;
	
	private int timer;

	public static Publisher[] clients;

	public static JFrame[] client_frame;
	
	public static AutoSubscriber auto_subscriber;
	
	public static JFrame auto_subscriber_frame;
	
	public static FaultManager faultManager;

	private int[][] publish_time;
	
	// time the GEMOM clients should update their setting by loading new time segment descriptor
	// negative value suggests publish tasks in each time segment has finished/not started
	private int[][] update_seg_time;
	
	private boolean[][] finished;
	 
	// the index of time segments each task has progressed to
	public int[][] clientProgress;
	
	private int max;
	
	private int min;
	
	private double time_step;   // time interval in seconds
	
	private double sleep_time;   // time interval in milli-seconds
	
	private ScenarioFileParser scenarioParser;
	
	private boolean stop;
	
	private boolean scenarioSet;
	
	private JTree tree;
	
	private File file;
	
	protected DateFormat m_sdf = SimpleDateFormat
	.getTimeInstance(SimpleDateFormat.MEDIUM);
	
	private DefaultTreeModel treeModel;
	
	protected JSplitPane splitPane;
	
	protected JScrollPane scrollPane = new JScrollPane();
	protected JScrollPane monitorScrollPane = new JScrollPane();

	protected JButton switchButton;
	protected JButton openButton;	
	protected JButton buildButton;	
	protected JButton startButton;
	protected JButton stopButton;
	
	protected JTextArea monitorTextArea = new JTextArea();
	
	private Thread thread;
	
	private int jobMode = -1;
	private String role = "";
	
	private Menu fm;
	private Menu rm;   // role mode menu
	private Menu sm;
	private Menu am;
	
	
	public static void main(String[] args) {
		ClientControl c = new ClientControl();
	}

	public ClientControl(){
		
		super("Scenario Test Harness");
		
		stop = false;
		scenarioSet = false;
		
		//set the menu bar
		MenuBar mb=new MenuBar();
		setMenuBar(mb); 

		// set the File menu
		fm= new Menu("File");
		mb.add(fm);
		MenuItem mi;
		
		// open menu item
		fm.add(mi = new MenuItem("Open", new MenuShortcut('O')));
		mi.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				loadScenario();
			}
		}
		);
		
		// close menu item
		fm.add(mi = new MenuItem("Exit", new MenuShortcut('X')));
		mi.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				System.exit(0);
			}
		}
		);
		
		// set the File menu
		rm= new Menu("Switch");
		mb.add(rm);
		
		// open menu item
		rm.add(mi = new MenuItem("Select", new MenuShortcut('E')));
		mi.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				resetAll();
				switchMode();
			}
		}
		);

		// The Scenario Menu...
		sm = new Menu("Scenario");
		mb.add(sm);

		// start menu item
		sm.add(mi = new MenuItem("Start", new MenuShortcut('S')));
		mi.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				startButton.setEnabled(false);
				stopButton.setEnabled(true);
				startPublishers();
			}
		}
		);

		// Pause menu item
		sm.add(mi = new MenuItem("Pause", new MenuShortcut('P')));
		mi.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				//pausePublish();

			}
		}
		);

		// Resume menu item
		sm.add(mi = new MenuItem("Stop", new MenuShortcut('R')));
		mi.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				stopPublishers();
				stopButton.setEnabled(false);
				buildButton.setEnabled(true);
			}
		}
		);


		// The About Menu..
		am = new Menu("Help");
		mb.add(am);
		am.add(mi = new MenuItem("About"));
		mi.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {

			}
		}
		);

		am.add(mi = new MenuItem("Credit"));
		mi.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {

			}
		}
		);
		
		
		DefaultMutableTreeNode top = new DefaultMutableTreeNode("Empty Setting");
		tree = new JTree(top);

		tree.addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent event) {
				if (((event.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
						&& (tree.getSelectionCount() > 0)) {
					//showMenu(event.getX(), event.getY());
				}
			}
		});
		
		tree.setCellRenderer(new Renderer());
		tree.addTreeSelectionListener(new TreeHandler());
		scrollPane.getViewport().add(tree);
		//getContentPane().add("North", textField);

		// set up the button panel
		JPanel panel = new JPanel(new GridLayout(3, 2));

		switchButton = new JButton("Switch role");
		switchButton.addActionListener(new java.awt.event.ActionListener(){
			public void actionPerformed(ActionEvent event) {
				resetAll();
				switchMode();
			}
		});
		
		
		openButton = new JButton("Open");
		openButton.addActionListener(new java.awt.event.ActionListener(){
			public void actionPerformed(ActionEvent event) {
				loadScenario();
			}
		});
		
		buildButton = new JButton("Build");
		buildButton.addActionListener(new java.awt.event.ActionListener(){
			public void actionPerformed(ActionEvent event) {
				init();
				startButton.setEnabled(true);
				stopButton.setEnabled(false);
				buildButton.setEnabled(false);

			}
		});
		
		startButton = new JButton("Start");
		startButton.addActionListener(new java.awt.event.ActionListener(){
			public void actionPerformed(ActionEvent event) {
				//divideSelectedItems(true,null);
				startButton.setEnabled(false);
				stopButton.setEnabled(true);
				start();
			}
		});

		stopButton = new JButton("Stop");
		stopButton.addActionListener(new java.awt.event.ActionListener(){
			public void actionPerformed(ActionEvent event) {
				stopButton.setEnabled(false);
				buildButton.setEnabled(true);
				stop();

			}
		});
		
		buildButton.setEnabled(false);
		startButton.setEnabled(false);
		stopButton.setEnabled(false);
		
		panel.add(switchButton);
		panel.add(openButton);
		panel.add(buildButton);
		panel.add(startButton);
		panel.add(stopButton);

		getContentPane().setLayout(new BorderLayout());

		monitorScrollPane.getViewport().add(monitorTextArea);
		
		getContentPane().add("South", panel);
		
		splitPane= new JSplitPane(JSplitPane.VERTICAL_SPLIT, monitorScrollPane, scrollPane);
		splitPane.setOneTouchExpandable(true);
		splitPane.setDividerLocation(150);
		
		getContentPane().add("Center", splitPane);

		// set up the list panel

		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		pack();
		setLocationRelativeTo(null); 

		setSize(600, 600);
		setVisible(true);
		
		Dimension scr = Toolkit.getDefaultToolkit().getScreenSize(); 
		Dimension frm = this.getSize(); 
		setLocation( (scr.width - frm.width) / 2, 
				(scr.height - frm.height)/6); 
		
		
		switchMode();
	}
	
	private void switchMode(){
		ModeDialog dialog = new ModeDialog(this);

		dialog.setVisible(true);
		this.jobMode= dialog.getMode();

		if(jobMode == 0){
		    role = "AutoPublisher";
		    // enable the scenario pad
		    sm.setEnabled(true);
		    if(scenarioSet)
		        buildButton.setEnabled(true);
		    else
			    buildButton.setEnabled(false);
			startButton.setEnabled(false);
			stopButton.setEnabled(false);
		}
		if(jobMode == 1){
			// enable the scenario pad
		    role = "AutoSubscriber";
		    sm.setEnabled(false);
		    if(scenarioSet)
		        buildButton.setEnabled(true);
		    else
			    buildButton.setEnabled(false);
			startButton.setEnabled(false);
			stopButton.setEnabled(false);
		}
		if(jobMode == 2){
		    role = "Fault Manager";
		    sm.setEnabled(false);
		    if(scenarioSet)
		        buildButton.setEnabled(true);
		    else
			    buildButton.setEnabled(false);
			startButton.setEnabled(false);
			stopButton.setEnabled(false);
		}
		printLineInMonitor("Client role switched to " + role);

	}
	
	private void resetAll(){
		if(jobMode == 0){
    	    for(int i=0; i<client_num; i++)
	        {
    	    	if(client_frame[i] != null){
	    	    client_frame[i].dispose();
    	    	client_frame[i] = null;
    	    	}
	        }
		}
		if(jobMode == 1){
            if(auto_subscriber_frame != null){
	    	    auto_subscriber_frame.dispose();
                auto_subscriber_frame = null;
            }
		}
		if(jobMode == 2){
           // if(auto_subscriber != null)
	       //    auto_subscriber.dispose();
		}
	}
	
	private boolean loadScenario(){
		
		DefaultMutableTreeNode top;

		JFileChooser fc=new JFileChooser();

		XMLfilter filter = new XMLfilter();

		fc.setFileFilter(filter);

		int returnVal = fc.showOpenDialog(this);

		if (returnVal == JFileChooser.APPROVE_OPTION) {
			file = fc.getSelectedFile();
		
		    scenarioParser= new ScenarioFileParser(file);

		    buildButton.setEnabled(true);
		    startButton.setEnabled(false);
		    stopButton.setEnabled(false);
		    
		    setupTree();
		    
		    printLineInMonitor(getTimeAsString() + ": Scenario loaded");
		    
		    scenarioSet = true;
		    
		    return true;
		}
		else
		{
			return false;
		}
		
	}
	
	public class apThread extends Thread{
	    public void run() {    

	    	doPublish();
	    	
	    	// publish control messages to the broker overlaymanager, used for signaling
	    	// endings of tasks of all GEMOM publishers
	    	clients[0].publish(DemoResource.CONTROL_NAMESPACE,
	    			DemoResource.PUBLISHER_STATUS_TOPIC,
	    			DemoResource.PUBLISHER_STATUS_NAME,
	    			DemoResource.PUBLISHER_STATUS_FINISHED);
	    	for(int i=0; i<client_num; i++)
		    {
		    	client_frame[i].dispose();
		    	client_frame[i] = null;
		    }
	    	return;
	    }
	}
	
	private void init(){
		if(jobMode == 0){
    	    initPublishers();
		}
		if(jobMode == 1){
			initSubscriber();
		}
		if(jobMode == 2){
            initFaultManager();
		}
	
	}
	
	private void start(){
		if(jobMode == 0){
    	    startPublishers();
		}
		if(jobMode == 1){
			startSubscriber();
		}
		if(jobMode == 2){
            startFaultManager();
		}
		
	}
	
	private void stop(){
		if(jobMode == 0){
    	    stopPublishers();
		}
		if(jobMode == 1){
			auto_subscriber_frame.dispose();
			auto_subscriber_frame = null;
		}
		if(jobMode == 2){
			faultManager.dispose();
			faultManager = null;
		}
		
	}
	
	private void initSubscriber(){
		Subscription sub = new Subscription("default host");
		for(int i = 0; i < scenarioParser.nsList.size(); i ++){
		    sub.addNamespace(scenarioParser.nsList.get(i));
		}
		
		this.auto_subscriber = new AutoSubscriber(1, null);
		this.auto_subscriber.initialiseStartupParameters(scenarioParser, sub);
		auto_subscriber_frame = new JFrame("GEMOM Auto-Subscriber");

		auto_subscriber_frame.getContentPane().add(auto_subscriber);
		auto_subscriber_frame.setBounds(10, 10, 1200, 600);
    	auto_subscriber_frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    	auto_subscriber_frame.setVisible(true);
		Dimension scr = Toolkit.getDefaultToolkit().getScreenSize(); 
		Dimension frm = auto_subscriber_frame.getSize(); 
		auto_subscriber_frame.setLocation( (scr.width - frm.width) / 2, 
				(scr.height - frm.height)/6); 
		
	}
	
	private void startSubscriber(){	
		this.auto_subscriber.initSubscription();	
	}
	
	private void initFaultManager(){
		Subscription sub = new Subscription("default host");
		for(int i = 0; i < scenarioParser.nsList.size(); i ++){
		    sub.addNamespace(scenarioParser.nsList.get(i));
		}
		
		this.faultManager = new FaultManager(1, sub);
		faultManager.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		faultManager.setVisible(true);
	}
	
	private void startFaultManager(){	
		//this.faultManager.initFaultManager();	
	}
	
	// loop to publishing
	private void doPublish(){
		
		printLineInMonitor(getTimeAsString() + ": Scenario started");
		
		//printStat();
		
		// the timer is a measurement of number of time steps that have passed:
		// one time steps = sleep time
		timer = 0;
		
		clients[0].publish(DemoResource.CONTROL_NAMESPACE,
    			DemoResource.PUBLISHER_STATUS_TOPIC,
    			DemoResource.PUBLISHER_STATUS_NAME,
    			DemoResource.PUBLISHER_STATUS_STARTING);
		
		while(true)
		{
	    	clients[0].publish(DemoResource.CONTROL_NAMESPACE,
	    			DemoResource.PUBLISHER_STATUS_TOPIC,
	    			DemoResource.PUBLISHER_STATUS_NAME,
	    			DemoResource.PUBLISHER_STATUS_SENDING);
	    	
			long timeBefore = System.currentTimeMillis();
			for(int i=0; i<client_num; i++)
			{
				for(int j=0; j<scenarioParser.getPublisherSetting(i).getTaskNum(); j++){
					// do publish task
					if(publish_time[i][j]==timer && !finished[i][j])
					{
						// check if a client is still actively engaged in a task 
						if(clients[i].active){
							//load the current time segment
							int seg_index = clientProgress[i][j];
							TimeSeg seg = scenarioParser.getPublisherSetting(i).getTask(j).get(seg_index);
						    clients[i].setTaskByTimeSeg(seg);
							
							clients[i].onPublish(timer);
							updatePublishTime(timer, i, j);
						}	
					}
					
					// check if client activity profile should be updated with new settings
					if(update_seg_time[i][j] == timer && !finished[i][j]){
						updateTasks(timer, i, j);
					} 
				}
			}		
			

			if(check()){
				stop = true;
				printLineInMonitor(getTimeAsString() + ": Scenario finished");
			}

			long timeAfter = System.currentTimeMillis(); 
			
			double sleepTime ;
			
			if(sleep_time-timeAfter+timeBefore > 0){
			
				// make out the difference between time interval between two consecutive 
				// publish actions and time just used on publishing, the result is the time to
				// wait precisely before next publish. Note the -1 deduction is used to account
				// for further overhead used on calculating the difference and invoke necessary
				// sleep thread routines (an inaccurate, dirty, but maybe effective way to make
				// sure the message rate is not lower than required).
			    sleepTime = sleep_time-timeAfter+timeBefore-1;
				
			    waiting(sleepTime);
			}
			
			else{
				printLineInMonitor("AutoPublisher Maximum Message Rate (All Clients Combined) Reached," +
						" lower publisher message rate.");
			}
			
			timer++;
			if(stop)
				break;
		}

	}
	
	public int getTimeSlot(){
		return timer;
	}

	// wait a constant time
	private void waiting(double time){
		
		try{
			Thread.sleep((int)time);
		}catch (Exception e){
			
		}

	}

	
	// load first time segments of each tasks that belong to all publishers
	private void initTask(){
		
		for(int i=0; i<client_num; i++)
		{
			
			int taskNum = scenarioParser.getPublisherSetting(i).getTaskNum();
			
			for(int j = 0; j < taskNum; j++){
				
				finished[i][j] = false;

				int time = (int)(scenarioParser.getPublisherSetting(i).getTask(j).get(0).time_start/time_step);

				// set the time of when publisher's settings should change
				
	            update_seg_time[i][j]=(int)(scenarioParser.getPublisherSetting(i).getTask(j).get(0).time_stop/time_step);
	            
				updateTasks(time, i, j);
			
			}
		}
	}

	private boolean check(){
		boolean done = true;
		for(int i=0; i<client_num; i++)
		{
            int taskNum = scenarioParser.getPublisherSetting(i).getTaskNum();
			
			for(int j = 0; j < taskNum; j++){
			    if(!finished[i][j])
			        done = false;
			}
		}
		return done;
	}
	
	private void initPublishers(){
		
        this.client_num = scenarioParser.publishers.size();
		
        // this is a dirty but quick solution allowing one single publisher to have multiple 
        // parrellel tasks by assigning an 2D array which keeps the number of tasks of each 
        // publisher. The size of the array in first 2nd dimension only reflects the 
        // number of tasks of the publisher that has got most tasks. To check how many
        // tasks each publisher has, one has to call getTaskNum() metohd of the
        // publisherSetting class
        
        // find maximum number of tasks
        int max_task_num = 0;
        for(int i = 0; i < client_num; i ++){
        	int task_num = scenarioParser.getPublisherSetting(i).getTaskNum();
            if(task_num>max_task_num)
            	max_task_num = task_num;
        }
    	publish_time = new int[client_num][max_task_num];

    	update_seg_time = new int[client_num][max_task_num];
	
    	finished = new boolean[client_num][max_task_num];
    	
    	clientProgress = new int[client_num][max_task_num];
	
    	//max=scenarioParser.max_rate;

    	//min=scenarioParser.min_rate;

	    max=scenarioParser.max_rate;

	    min=scenarioParser.min_rate;
	      		
	    this.time_step = ((double)(scenarioParser.time_step))/1000.0;  // in seconds
	
	    this.sleep_time = (double)scenarioParser.time_step;   // in milli-seconds
	    
	    clients= new Publisher[client_num];
	    client_frame = new JFrame[client_num];
	
	    for(int i=0; i<client_num; i++)
	    {
	    	
	    	// dirty implementation: when initialising a publisher the first time segment of the first
	    	// task belonging to this publisher is used. This however has no effect on the autopublisher
	    	// as the task profile for autopublishing process is always inserted just before publishing
	    	clients[i]= new Publisher(i, scenarioParser.getPublisherSetting(i).getTask(0).get(0), this);

	    	int j=i+1;   
		
	    	client_frame[i]= new JFrame("GEMOM Client"+j);

	    	client_frame[i].getContentPane().add(clients[i]);
	    	client_frame[i].setBounds(10, 10, 950, 550);
	    	client_frame[i].setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
	    	client_frame[i].setVisible(true);
	    }
	    
	    /*
        new Thread()
        {
          public void run()
          {
      	    String[] command = new String[1];
    	    command[0]="java -jar gemom.jar";
    	    //"//C:\\Documents and Settings\\Administrator\\Desktop\\java -jar gemom.jar";
    	    //command[0]="C:\\Documents and Settings\\Administrator\\Desktop\\gp373w32\\wgnupl32.exe";
    	    
    	    exec(command);
          }
        }
        .start
          ();

        */
	    //initSubscriber();
	    
	    //auto_subscriber.initSubscribe();
	    
	    printLineInMonitor(getTimeAsString() + ": Scenario ready to go"); 
	}
	
	private void startPublishers(){
    	/*
	    for(int i=0; i<client_num; i++)
     	{
	     	printStory(clients[i], i);
	        printInClient(clients[i], i);
	    }
	    */
		initTask();

	    thread = new apThread();
	    
	    stop = false;
	    // Start the thread
	    thread.start();

		//doPublish();
	}
	
	private void stopPublishers(){
		stop = true;
		printLineInMonitor(getTimeAsString() + ": Scenario stopped");
	}
	
	// check whether there are more time segments description for the specific publisher, load
	// the new segment whenever it applies and set the new update time tag, publish time tag, finished
	// flag and set them as finished when there are no more time tasks for the publisher
	public void updateTasks(int time_slot, int publisher_index, int task_index){
		
		if(update_seg_time[publisher_index][task_index] == time_slot){
			int seg_index = clientProgress[publisher_index][task_index];
			
			// check if there is no more future settings for a client, if so signal the
			// client should have finished its task by setting finished[client_index] to 
			// true
			if(scenarioParser.getPublisherSetting(publisher_index).getTask(task_index).size() <= seg_index+1){
				update_seg_time[publisher_index][task_index] = -1;
			    finished[publisher_index][task_index] = true;
			    publish_time[publisher_index][task_index] = -1;
			}
			else{
			// There are certainly more future settings stored in the form of
			// next time segment, the subroutine then loads new time segment settings
			
				TimeSeg seg = scenarioParser.getPublisherSetting(publisher_index).getTask(task_index).get(seg_index+1);
			    //The following method is now invoked in doPublish() method of the apThread
				//clients[publisher_index].updateGEMOMDemo(seg);
				clientProgress[publisher_index][task_index]++;
			    update_seg_time[publisher_index][task_index] = (int)(seg.time_stop/time_step);
			    updatePublishTime((int)(seg.time_start/time_step), publisher_index, task_index);
			}
		}
		else{
			updatePublishTime(time_slot, publisher_index, task_index);
		}
	}

    private void updatePublishTime(int time_slot, int publisher_index, int task_index){
				
		if(!finished[publisher_index][task_index]){
		
		    double y = 0;
		    // get the next publish time which has the time interval follows an exponential distribution
		    if(clients[publisher_index].getDistribution() == 0)
		         y=Util.getNextPublishTimeExp(clients[publisher_index].Message_rate);
		    if(clients[publisher_index].getDistribution() == 1)
		         y=Util.getNextPublishTimeUni(clients[publisher_index].Message_rate);
			
		    // the relative next publish time has to be transformed into a time slot indicator 
		    int nexttime=(int)(y/time_step+1);
		
	    	publish_time[publisher_index][task_index] = time_slot+nexttime;
		}
	}
    
	private void setupTree()
	{
		DefaultMutableTreeNode root = scenarioParser.getSaxTree().getTree();
		
		//DefaultMutableTreeNode rootClone = (DefaultMutableTreeNode)root.clone();
		
		//DefaultMutableTreeNode root=(DefaultMutableTreeNode) tree.getModel().getRoot(); 
		//root.removeAllChildren();
		//addAll(root,top);

		treeModel=new DefaultTreeModel(root);
		
		tree.setModel(treeModel);
		
		expandAll(tree, new TreePath(root),true);
		tree.getParent().repaint();
	}
	
	private void expandAll(JTree tree, TreePath parent, boolean expand) {
		// Traverse children
		DefaultMutableTreeNode node = (DefaultMutableTreeNode)parent.getLastPathComponent();
		if (node.getChildCount() >= 0) {
			for (Enumeration e=node.children(); e.hasMoreElements(); ) {
				DefaultMutableTreeNode n = (DefaultMutableTreeNode)e.nextElement();
				TreePath path = parent.pathByAddingChild(n);
				expandAll(tree, path, expand);
			}
		}
		
		// Expansion or collapse must be done bottom-up
		if (expand) {
			tree.expandPath(parent);
		} else {
			tree.collapsePath(parent);
		}
	}
    
	void printInMonitor(String msg) {
		monitorTextArea.append(msg);
	}

	void printLineInMonitor(String msg) {
		printInMonitor(msg);
		monitorTextArea.append("\n");
	}

	void printInSubMonitor(String msg) {
	    //subMonitorTextArea.append(msg);
	}

	void printLineInSubMonitor(String msg) {
		printInSubMonitor(msg);
		//subMonitorTextArea.append("\n");
	}
	
	protected String getTimeAsString() {
		return m_sdf.format(new Date());
	}
	
	// print information on client, used for testing
	private void printStat(){
		printLineInMonitor("Max rate = " + scenarioParser.max_rate);
		printLineInMonitor("Min rate = " + scenarioParser.min_rate);
		printLineInMonitor("Time step = " + scenarioParser.time_step);
		for(int j=0; j<client_num; j++){
		    printLineInMonitor("publish_time = " + publish_time[j]);
		    printLineInMonitor("update_time = " + update_seg_time[j]);
		}
	}
	
	// print information on client, used for testing
	private void printStoryInClient(Publisher client, int index){  
		client.printLineInMonitor("no. of tasks = " + scenarioParser.getPublisherSetting(index).getTaskNum());
		
		for(int i = 0; i < scenarioParser.getPublisherSetting(index).getTaskNum(); i++){
			client.printLineInMonitor("Client task No."+(i+1));
			
			client.printLineInMonitor("no. of time seg = " + scenarioParser.getPublisherSetting(index).getTask(i));
			
			for(int j = 0; j < scenarioParser.getPublisherSetting(index).getTask(i).size(); j++){
		
				TimeSeg seg = scenarioParser.getPublisherSetting(index).getTask(i).get(j);
				client.printLineInMonitor("mean rate = " + seg.mean_rate);
				client.printLineInMonitor("time_start = " + seg.time_start);
				client.printLineInMonitor("topicCns = " + seg.topicns);
				client.printLineInMonitor("distribution = " + seg.distribution);
				client.printLineInMonitor("node = " + seg.ingressNode);
			}
		}

		 	
	}
	
	public class TreeHandler implements TreeSelectionListener {
		public void valueChanged(TreeSelectionEvent e) {
			//TreePath path = e.getPath();
			//String text = path.getPathComponent(path.getPathCount() - 1).toString();
			//String text=Datasaver.pathtransfer(path.toString());
			//textField.setText(text);
		}
	}
	
	
	public Boolean exec(String[] command) {
		Process proc;
		StringBuffer cmdStr= new StringBuffer();
		for(int i=0; i<command.length; i++){
		cmdStr.append(command[i].trim());
		if(i!=command.length-1){
		cmdStr.append(" ");
		}
		}

		try {
		proc = Runtime.getRuntime().exec(cmdStr.toString());
		} catch (IOException e) {
			printLineInMonitor("EXCEPTION_IO" + command);
			printLineInMonitor("EXCEPTION_TRACE"+ e);
		return new Boolean(false);
		}
		int exitStatus;
		while (true) {
		try {
		exitStatus = proc.waitFor();
		break;
		} catch (java.lang.InterruptedException e) {
			printLineInMonitor("EXCEPTION_TRACE"+ e);
		}
		}
		if (exitStatus == 0) {
		InputStream is = proc.getInputStream();
		readStreamData(is);
		}
		if (exitStatus != 0) {
		InputStream e = proc.getErrorStream();
		String error = readStreamData(e);
		printLineInMonitor("ERROR_COMMAND"+ error);
		}
		return new Boolean(exitStatus == 0);
		}

		private static String readStreamData(InputStream e){
		BufferedInputStream bre = new BufferedInputStream(e);
		String output = new String(" ");
		try {
		int readInt = bre.read();
		while (readInt != -1) {
		char c = (char) readInt;
		output = output + c;
		readInt = bre.read();
		}
		} catch (IOException e1) {
		e1.printStackTrace();
		}
		return output;
		} 
		
}

class XMLfilter extends javax.swing.filechooser.FileFilter{
	public boolean accept(File f) {
		return f.isDirectory() || f.getName().toLowerCase().endsWith(".xml");
	}

	public String getDescription() {
		return ".xml files";
	}
}

class Renderer extends JLabel implements TreeCellRenderer{
	public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected,
			boolean expanded, boolean leaf, int row, boolean hasFocus) {
		//String nsvalue= Datasaver.pathtransfer(value.toString());
		setText(value.toString() + "                   ");
		return this;
	}
}
